package com.snuproject.lgt.imgtrans;


import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.snuproject.lgt.R;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;

public class GL3DImageRenderer implements Renderer {

    private int mWidth;
    private int mHeight;
    private float mEyeDistance = 0.1f;
    private float[] mFocusPoint = {0.0f, 0.0f, 1.0f};

    private static float ROTATION_SPEED = 1.5f;
    private float mAngle = 0;
    private float mAngleOffset = ROTATION_SPEED;
    
    private float mAniOff = 0;
    
    TextureFactory textureFactory;

    public GL3DImageRenderer(Context context) {
        //mContext = context;
        textureFactory = new TextureFactory(context);
    }

    private int mHeartTextureID = -1;
    private int mMoonTextureID = -1;

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        /*
         * By default, OpenGL enables features that improve quality but reduce
         * performance. One might want to tweak that especially on software
         * renderer.
         */
        gl.glDisable(GL10.GL_DITHER);

        /*
         * Some one-time OpenGL initialization can be made here probably based
         * on features of this particular context
         */
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

        gl.glClearColor(.0f, .0f, .0f, .0f);
        gl.glShadeModel(GL10.GL_FLAT);
        gl.glDisable(GL10.GL_LIGHTING);
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glFrontFace(GL10.GL_CCW);

//        Bitmap person = GlobalValues.getInstance().getPerson();
//        if( person != null ) {
//        	Bitmap person_trans = Bitmap.createScaledBitmap(person, 256, 256,true);
//        	mPersonTextureID = textureFactory.createTexture(gl, person_trans);
//        }
        mHeartTextureID = textureFactory.createTextureFromResource(gl, R.drawable.heart_arrow);
        mMoonTextureID = textureFactory.createTextureFromResource(gl, R.drawable.moon);
    }


    public void onSurfaceChanged(GL10 gl, int width, int height) {
        mWidth = width;
        mHeight = height;

        gl.glViewport(0, 0, mWidth, mHeight);

        float ratio = (float) width / height;
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    private void setLeftEnv(GL10 gl) {
        gl.glViewport(0, 0, (int) mWidth / 2, (int) mHeight);
  
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
        GLU.gluLookAt(gl, - mEyeDistance, 0.0f, 4.5f, mFocusPoint[0], mFocusPoint[1], mFocusPoint[2], 0.0f, 1.0f, 0.0f);
    }

    private void setRightEnv(GL10 gl) {
        gl.glViewport((int) mWidth / 2, 0, (int) mWidth / 2, (int) mHeight);

        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
        GLU.gluLookAt(gl, mEyeDistance, 0.0f, 4.5f, mFocusPoint[0], mFocusPoint[1], mFocusPoint[2], 0.0f, 1.0f, 0.0f);
    }

    public void onDrawFrame(GL10 gl) {
        mAngle += mAngleOffset;
        if (mAniOff >= 2000.0f)
        	mAniOff = 0.0f;
        else
        	mAniOff += 1.0f;
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        setLeftEnv(gl);
        draw(gl);
        setRightEnv(gl);
        draw(gl);
    }

    private void draw(GL10 gl) {
    	// draw person
//    	if( mPersonTextureID != -1 ) {
//            gl.glPushMatrix();
//            gl.glTranslatef(0.0f, -0.1f, -3.0f);
//            gl.glScalef(12.0f, 9.0f, 1.0f);
//            textureFactory.draw(gl, mPersonTextureID);
//            gl.glPopMatrix();
//    	}
    	
//        // draw earth
//        gl.glPushMatrix();
//        gl.glTranslatef(0.0f, 0.0f, -2.0f);
//        gl.glScalef(3.0f, 3.0f, 3.0f);
//        textureFactory.draw(gl, mEarthTextureID);
//        gl.glPopMatrix();
    	
    	int effectIndex = GlobalValues.getInstance().getEffectIndex();
    	
    	if( effectIndex == 0 ) 
    	{
    		float time = mAniOff%200.0f;
          // draw heart
          gl.glPushMatrix();
          gl.glTranslatef(-5.0f, +4.0f, -2.0f);
          if( time < 100 )
        	  gl.glScalef(2.5f - time/100.0f, 2.5f - time/100.0f, 1.0f);
          else
        	  gl.glScalef(1.5f + (time%100)/100.0f, 1.5f + (time%100)/100.0f, 1.0f);
        	  
          textureFactory.draw(gl, mHeartTextureID);
          gl.glPopMatrix();
          
          
          time = (mAniOff+50)%240.0f;
          // draw heart
          gl.glPushMatrix();
          gl.glTranslatef(4.5f, +4.0f, -2.0f);
          if( time < 120 )
        	  gl.glScalef(2.3f - time/200.0f, 2.3f - time/200.0f, 1.0f);
          else
        	  gl.glScalef(1.7f + (time%120)/200.0f, 1.7f + (time%120)/200.0f, 1.0f);
        	  
          textureFactory.draw(gl, mHeartTextureID);
          gl.glPopMatrix();

          time = (mAniOff+30)%240.0f;
          // draw heart
          gl.glPushMatrix();
          gl.glTranslatef(-2.0f, -2.0f, -2.0f);
          if( time < 120 )
        	  gl.glScalef(2.3f - time/200.0f, 2.3f - time/200.0f, 1.0f);
          else
        	  gl.glScalef(1.7f + (time%120)/200.0f, 1.7f + (time%120)/200.0f, 1.0f);
        	  
          textureFactory.draw(gl, mHeartTextureID);
          gl.glPopMatrix();

          time = (mAniOff+150)%240.0f;
          // draw heart
          gl.glPushMatrix();
          gl.glTranslatef(-8.0f, 0.0f, -2.0f);
          if( time < 120 )
        	  gl.glScalef(2.3f - time/200.0f, 2.3f - time/200.0f, 1.0f);
          else
        	  gl.glScalef(1.7f + (time%120)/200.0f, 1.7f + (time%120)/200.0f, 1.0f);
        	  
          textureFactory.draw(gl, mHeartTextureID);
          gl.glPopMatrix();

          time = (mAniOff+220)%240.0f;
          // draw heart
          gl.glPushMatrix();
          gl.glTranslatef(8.0f, -3.0f, -2.0f);
          if( time < 120 )
        	  gl.glScalef(2.3f - time/200.0f, 2.3f - time/200.0f, 1.0f);
          else
        	  gl.glScalef(1.7f + (time%120)/200.0f, 1.7f + (time%120)/200.0f, 1.0f);
        	  
          textureFactory.draw(gl, mHeartTextureID);
          gl.glPopMatrix();
    	}

    	if( effectIndex == 1 ) {
	        // draw moon
	        gl.glPushMatrix();
	        gl.glTranslatef(20.0f - (mAniOff%200)/5, 10.0f - (mAniOff%200)/15f, -2.0f);
	        gl.glScalef(0.7f, 0.7f, 0.7f);
	        gl.glRotatef(mAngle, 0, 1, 0);
	        textureFactory.drawS(gl, mMoonTextureID);
	        gl.glPopMatrix();
	
	        // draw moon
	        gl.glPushMatrix();
	        gl.glTranslatef(25.0f - (mAniOff%100)/2, 5.0f - (mAniOff%100)/20f, -1.0f);
	        gl.glScalef(0.7f, 0.7f, 0.7f);
	        gl.glRotatef(mAngle, 0, 1, 0);
	        textureFactory.drawS(gl, mMoonTextureID);
	        gl.glPopMatrix();
	
	        // draw moon
	        gl.glPushMatrix();
	        gl.glTranslatef(40.0f - (mAniOff%400)/5, 20.0f - (mAniOff%400)/15f, -5.0f);
	        gl.glScalef(0.7f, 0.7f, 0.7f);
	        gl.glRotatef(mAngle, 0, 1, 0);
	        textureFactory.drawS(gl, mMoonTextureID);
	        gl.glPopMatrix();
	
	        // draw moon
	        gl.glPushMatrix();
	        gl.glTranslatef(40.0f - (mAniOff%400)/5, 40.0f - (mAniOff%400)/5f, -5.0f);
	        gl.glScalef(0.7f, 0.7f, 0.7f);
	        gl.glRotatef(mAngle, 0, 1, 0);
	        textureFactory.drawS(gl, mMoonTextureID);
	        gl.glPopMatrix();
        
    	}
        

        
        //gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        //gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

    }
}

class TextureFactory {
	
	private Context mContext;

    private FloatBuffer mVertexBuffer;
    private FloatBuffer mTextureBuffer;
    private int mVerticeCount;

    private FloatBuffer mVertexBufferS;
    private FloatBuffer mTextureBufferS;
    private int mVerticeCountS;
    
    public TextureFactory(Context context) {
    	mContext = context;
    	
    	createBox();
    	createSphere(0, 0, 0, 1, 16);
    }
    

    private static float TWOPI = 6.28318530717958f;
    private static float PIDIV2 = 1.57079632679489f;
    void createSphere(float cx, float cy, float cz, float r, int n) {
        float theta1 = 0.0f;
        float theta2 = 0.0f;
        float theta3 = 0.0f;

        float[] e = { 0, 0, 0 };
        float[] p = { 0, 0, 0 };
        float[] t = { 0, 0 };

        if (r < 0)
            r = -r;

        if (n < 4)
            n = 4;
        mVerticeCountS = (n / 2) * ((n + 1) * 2);
        final int FLOAT_SIZE = 4;
        mVertexBufferS = ByteBuffer.allocateDirect(FLOAT_SIZE * mVerticeCountS * 3).order(
                ByteOrder.nativeOrder()).asFloatBuffer();
        mVertexBufferS.position(0);
        mTextureBufferS = ByteBuffer.allocateDirect(FLOAT_SIZE * mVerticeCountS * 2).order(
                ByteOrder.nativeOrder()).asFloatBuffer();
        mTextureBufferS.position(0);
        for (int i = 0; i < n / 2; ++i) {
            theta1 = i * TWOPI / n - PIDIV2;
            theta2 = (i + 1) * TWOPI / n - PIDIV2;

            for (int j = 0; j <= n; ++j) {
                theta3 = j * TWOPI / n;

                e[0] = (float) (Math.cos(theta2) * Math.cos(theta3));
                e[1] = (float) Math.sin(theta2);
                e[2] = (float) (Math.cos(theta2) * Math.sin(theta3));
                p[0] = cx + r * e[0];
                p[1] = cy + r * e[1];
                p[2] = cz + r * e[2];
                t[0] = (j / (float) n);
                t[1] = -2 * (i + 1) / (float) n;
                mVertexBufferS.put(p);
                mTextureBufferS.put(t);

                e[0] = (float) (Math.cos(theta1) * Math.cos(theta3));
                e[1] = (float) Math.sin(theta1);
                e[2] = (float) (Math.cos(theta1) * Math.sin(theta3));
                p[0] = cx + r * e[0];
                p[1] = cy + r * e[1];
                p[2] = cz + r * e[2];
                t[0] = (j / (float) n);
                t[1] = -2 * i / (float) n;
                mVertexBufferS.put(p);
                mTextureBufferS.put(t);
            }
        }
        mVertexBufferS.position(0);
        mTextureBufferS.position(0);
    }
	
    void createBox() {
    	float vertices[] = {
    			-1.0f, -1.0f,  0.0f,		// V1 - bottom left
    			-1.0f,  1.0f,  0.0f,		// V2 - top left
    			 1.0f, -1.0f,  0.0f,		// V3 - bottom right
    			 1.0f,  1.0f,  0.0f			// V4 - top right
    	};
    	float texture[] = {
    			// Mapping coordinates for the vertices
    			0.0f, 1.0f,		// top left		(V2)
    			0.0f, 0.0f,		// bottom left	(V1)
    			1.0f, 1.0f,		// top right	(V4)
    			1.0f, 0.0f		// bottom right	(V3)
    	};

    	ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
    	byteBuffer.order(ByteOrder.nativeOrder());
    	mVertexBuffer = byteBuffer.asFloatBuffer();
    	mVertexBuffer.put(vertices);
    	mVertexBuffer.position(0);
		
		byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
		byteBuffer.order(ByteOrder.nativeOrder());
		mTextureBuffer = byteBuffer.asFloatBuffer();
		mTextureBuffer.put(texture);
		mTextureBuffer.position(0);
		
		mVerticeCount = vertices.length/3;
    }
    
    public int createTexture(GL10 gl, Bitmap bitmap) {
        int[] textures = new int[1];
        gl.glGenTextures(1, textures, 0);

        int texture = textures[0];

        gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

        gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);

        Bitmap bitmap2;
        
        bitmap2 = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        {
        	int width = bitmap.getWidth();
        	int height = bitmap.getHeight();
        	
        	for ( int x = 0; x < width; x++ ) {
        		for ( int y = 0; y < height; y++ ) {
        			if(bitmap.getPixel(x, y) == Color.BLACK) {
        				bitmap2.setPixel(x, y, Color.TRANSPARENT);
        			} else {
        				bitmap2.setPixel(x, y, bitmap.getPixel(x, y));
        			}
        		}
        	}
        }

        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0);
        bitmap.recycle();
        return texture;
    }
    

    public int createTextureFromResource(GL10 gl, int resId) {
		InputStream is = mContext.getResources().openRawResource(resId);
		Bitmap bitmap = null;
		try {
			bitmap = BitmapFactory.decodeStream(is);
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				// ignore
			}
		}
		
		return createTexture(gl, bitmap);
    }
    
    public void draw(GL10 gl, int textureID) {
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mVerticeCount);
    }

    public void drawS(GL10 gl, int textureID) {
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBufferS);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBufferS);
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mVerticeCountS);
    }

}
